package com.xxl.job.executor.xxljob.autoregister.service;

import com.xxl.job.executor.xxljob.autoregister.enums.XxlExecutorRouteStrategyEnum;
import com.xxl.job.executor.xxljob.autoregister.model.XxlJobInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * @author lukaidong
 * @date 2024/02/29
 * @description
 */
public abstract class AbstractXxlJobAutoRegister implements IXxlJobAutoRegister {
    private static Logger logger = LoggerFactory.getLogger(AbstractXxlJobAutoRegister.class);

    private static final String XXL_JOB_REGISTER_LOCK_KEY = "xxlJobRegisterLockKey";
    private static final Long LOCK_TIME = 20L;
    public static final int TASK_OPEN_STATUS = 1;// 任务开启状态
    public static final String DEFAULT_SCHEDULE_TYPE = "CRON";  // 默认调度类型：cron表达式
    public static final String DEFAULT_MISFIRE_STRATEGY = "DO_NOTHING";// 默认调度过期策略：忽略
    public static final int DEFAULT_EXECUTOR_TIMEOUT = 0;            // 任务执行超时时间，单位秒
    public static final int DEFAULT_EXECUTOR_FAIL_RETRY_COUNT = 0;        // 失败重试次数
    public static final String DEFAULT_GLUE_TYPE = "BEAN";        // GLUE类型

    public static final String DEFAULT_EXECUTOR_BLOCK_STRATEGY = "DISCARD_LATER";// 阻塞处理策略：丢弃后续进度
//    public static final String DEFAULT_EXECUTOR_BLOCK_STRATEGY = "SERIAL_EXECUTION";// 阻塞处理策略：单机串行策略


    @Value("${xxl.job.executor.appname:导入导出中心执行器}")
    private String appname;   // 注意 appname 不能超过 64字
    @Value("${xxl.job.executor.title:导入导出中心执行器组}")
    private String title;     // 注意 title 不能超过 12字
    @Value("${xxl.job.auto.register.author:auto-register}")
    private String author;

    private Integer groupId;
    private volatile Boolean isMasterRegister = null;
    protected final RedisTemplate<String, String> redisTemplate;

    public AbstractXxlJobAutoRegister(RedisTemplate<String, String> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Override
    public void registerOrStartJob(String jobName, String cron, XxlExecutorRouteStrategyEnum routeStrategy) {
        if (!needRegisterJob()) {
            return;
        }
        // 1、获取到执行器组id
        if (this.groupId == null || this.groupId <= 0) {
            this.groupId = this.computeExecuteGroupIdIfAbsent(appname, title);
        }

        // 2、开启任务
        XxlJobInfo xxlJobInfo = this.selectJobByGroupAndHandler(groupId, jobName);
        if (xxlJobInfo != null) {
            // 可能因为其他原因修改了配置，旧不主动打开了
//            if (xxlJobInfo.getTriggerStatus() != TASK_OPEN_STATUS) {
//                this.scheduleUpdate(xxlJobInfo.getId(), TASK_OPEN_STATUS);
//            }
            return;
        }

        // 3、注册任务，任务状态：打开
        Date now = new Date();
        xxlJobInfo = new XxlJobInfo();
        xxlJobInfo.setJobGroup(groupId);
        xxlJobInfo.setJobDesc("async任务中心：" + jobName);// 任务描述
        xxlJobInfo.setAddTime(now);
        xxlJobInfo.setUpdateTime(now);
        xxlJobInfo.setAuthor(author);
        xxlJobInfo.setAlarmEmail(null);// 任务失败报警邮件
        xxlJobInfo.setScheduleType(DEFAULT_SCHEDULE_TYPE);// 调度类型 cron
        xxlJobInfo.setScheduleConf(cron);
        xxlJobInfo.setMisfireStrategy(DEFAULT_MISFIRE_STRATEGY);// 调度过期策略
        xxlJobInfo.setExecutorRouteStrategy(routeStrategy.getCode());// 路由策略
        xxlJobInfo.setExecutorHandler(jobName);// 任务处理器
        xxlJobInfo.setExecutorParam(null);//    执行器参数
        xxlJobInfo.setExecutorBlockStrategy(DEFAULT_EXECUTOR_BLOCK_STRATEGY);// 阻塞处理策略
        xxlJobInfo.setExecutorTimeout(DEFAULT_EXECUTOR_TIMEOUT);// 超时时间
        xxlJobInfo.setExecutorFailRetryCount(DEFAULT_EXECUTOR_FAIL_RETRY_COUNT);// 任务失败重试次数
        xxlJobInfo.setGlueType(DEFAULT_GLUE_TYPE);
        xxlJobInfo.setGlueSource(null);// GLUE源代码，我们是BEAN模式，不需要此参数
        xxlJobInfo.setGlueRemark(null);
        xxlJobInfo.setChildJobId(null);// 子任务，这里没有
        xxlJobInfo.setGlueUpdatetime(now);
        xxlJobInfo.setTriggerStatus(TASK_OPEN_STATUS);// 任务调度状态，开启
        xxlJobInfo.setTriggerLastTime(0);// 上次调度时间：初始化为 0
        xxlJobInfo.setTriggerNextTime(0);// 下次调度时间：初始化为 0

        this.saveJobInfo(xxlJobInfo);
    }

    /**
     * 获取执行器组，如果没有就创建
     *
     * @param appname 执行器应用名
     * @param title   标题
     * @return 执行器组id
     */
    protected abstract int computeExecuteGroupIdIfAbsent(String appname, String title);

    /**
     * 修改任务状态
     *
     * @param jobId         任务id
     * @param triggerStatus 目标任务状态
     */
    protected abstract void scheduleUpdate(int jobId, int triggerStatus);

    /**
     * 查询jobInfo
     *
     * @param groupId 执行器组id
     * @param jobName jon名字
     */
    protected abstract XxlJobInfo selectJobByGroupAndHandler(int groupId, String jobName);

    /**
     * 保存xxlJobInfo信息
     *
     * @param xxlJobInfo 任务信息
     */
    protected abstract void saveJobInfo(XxlJobInfo xxlJobInfo);

    /**
     * redis加锁成功就是主节点
     */
    protected boolean needRegisterJob() {
        if (isMasterRegister == null) {
            // 可能同一个jvm多线程执行，同时抢锁，后面加锁失败的会将前面加锁成功的锁标志改为false，因此在次做一个判断
            synchronized (this) {
                if (this.isMasterRegister == null) {
                    // redis加锁
                    Boolean lock = redisTemplate.opsForValue().setIfAbsent(XXL_JOB_REGISTER_LOCK_KEY, XXL_JOB_REGISTER_LOCK_KEY);
                    logger.info("xxl-job注册主节点 加锁结果：{}", lock);
                    if (lock != null && lock) {
                        redisTemplate.expire(XXL_JOB_REGISTER_LOCK_KEY, LOCK_TIME, TimeUnit.MINUTES);
                        this.isMasterRegister = true;
                    } else {
                        this.isMasterRegister = false;
                    }
                }
            }
        }
        return this.isMasterRegister;
    }

}
